package ren.steve.system.filter;

import com.alibaba.fastjson.JSON;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import ren.steve.common.result.Result;
import ren.steve.common.result.HttpStatus;
import ren.steve.common.utils.IpUtil;
import ren.steve.common.utils.JwtHelper;
import ren.steve.common.utils.ResponseUtil;
import ren.steve.model.system.vo.LoginVo;
import ren.steve.system.custom.CustomUser;
import ren.steve.system.service.LoginLogService;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

public class TokenLoginFilter extends UsernamePasswordAuthenticationFilter {

  private RedisTemplate redisTemplate;

  private LoginLogService loginLogService;

  // 构造方法
  public TokenLoginFilter(AuthenticationManager manager, RedisTemplate redisTemplate, LoginLogService loginLogService) {
    this.setAuthenticationManager(manager);
    this.setPostOnly(false);
    //指定登录接口及提交方式，可以指定任意路径
    this.setRequiresAuthenticationRequestMatcher(new AntPathRequestMatcher("/api/admin/login","POST"));
    this.redisTemplate = redisTemplate;
    this.loginLogService = loginLogService;
  }

  // 获取用户名和密码和认证
  public Authentication attemptAuthentication(
    HttpServletRequest request,
    HttpServletResponse response
  ) throws AuthenticationException {
    try {
      LoginVo loginVo = new ObjectMapper().readValue(request.getInputStream(), LoginVo.class);
      Authentication authenticationToken = new UsernamePasswordAuthenticationToken(loginVo.getAccount(), loginVo.getPassword());
      return this.getAuthenticationManager().authenticate(authenticationToken);
    } catch (IOException e) {
      e.printStackTrace();
    }
    return null;
  }

  // 认证成功调用方法
  @Override
  protected void successfulAuthentication(
    HttpServletRequest request,
    HttpServletResponse response,
    FilterChain chain,
    Authentication auth
  ) throws IOException, ServletException {
    // 获取认证的对象
    CustomUser customUser = (CustomUser) auth.getPrincipal();
    // 将用户数据缓存到 Redis 中
    redisTemplate.opsForValue().set(customUser.getUsername(), JSON.toJSONString(customUser.getAuthorities()));
    // 生成Token
    String token = JwtHelper.createToken(
      customUser.getSystemUser().getId(),
      customUser.getSystemUser().getCode(),
      customUser.getSystemUser().getEmail(),
      customUser.getSystemUser().getPhone()
    );
    //
    loginLogService.recordLoginLog(
      customUser.getSystemUser().getCode(),
      IpUtil.getIpAddress(request),
      "登录成功",
      "000"
    );

    // 返回
    Map<String, Object> map = new HashMap<>();
    map.put("token", token);
    ResponseUtil.out(response, Result.ok(map, HttpStatus.OK));
  }

  // 认证失败调用方法
  @Override
  protected void unsuccessfulAuthentication(
    HttpServletRequest request,
    HttpServletResponse response,
    AuthenticationException failed
  ) throws IOException, ServletException {
    if (failed.getCause() instanceof RuntimeException) {
      ResponseUtil.out(response, Result.build(null, HttpStatus.UNAUTHORIZED));
    } else {
      ResponseUtil.out(response, Result.build(null, HttpStatus.METHOD_NOT_ALLOWED));
    }
  }
}
